/**
 * Copyright 2015年3月27日 Wang Zheng
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author Wang Zheng ufo5260987423@163.com
 *
 */
package com.ufo5260987423.graphDatabase.core.GAO;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.ufo5260987423.graphDatabase.core.DAO.AbstractFactory;
import com.ufo5260987423.graphDatabase.core.DAO.Edge;
import com.ufo5260987423.graphDatabase.core.DAO.EdgeAttributeName;
import com.ufo5260987423.graphDatabase.core.DAO.EdgeAttributeNameFactory;
import com.ufo5260987423.graphDatabase.core.DAO.EdgeAttributeValue;
import com.ufo5260987423.graphDatabase.core.DAO.EdgeAttributeValueFactory;
import com.ufo5260987423.graphDatabase.core.DAO.EdgeFactory;
import com.ufo5260987423.graphDatabase.core.sqlTypes.SqlChars;
import com.ufo5260987423.graphDatabase.core.sqlTypes.SqlInteger;

/**
 * @ClassName: GraphEdgeFactory
 * @Description: TODO
 * @author ufo ufo5260987423@163.com
 * @date 2015年3月27日 下午8:55:03
 *
 */
public class GraphEdgeFactory {
	private EdgeFactory edgeFactory;
	private EdgeAttributeNameFactory edgeAttributeNameFactory;
	private EdgeAttributeValueFactory edgeAttributeValueFactory;

	public GraphEdgeFactory() {
		this.setEdgeFactory(new EdgeFactory());
		this.setEdgeAttributeNameFactory(new EdgeAttributeNameFactory());
		this.setEdgeAttributeValueFactory(new EdgeAttributeValueFactory());
	}

	public Boolean graphEdgeIsExist(Long graphEdgeId) {
		return this.getEdgeFactory().isExist(new SqlInteger<Long>((short) 16, graphEdgeId));
	}

	public Boolean graphEdgeAttributeNameIsExist(String graphEdgeAttributeName) {
		return null != this.getEdgeAttributeNameFactory().getBy(graphEdgeAttributeName);
	}

	public Boolean graphEdgeAttributeValueIsExist(Long edgeId, String graphEdgeAttribute) {
		return null != this.getEdgeAttributeValueFactory().getBy(edgeId,
				this.getEdgeAttributeNameFactory().getBy(graphEdgeAttribute).getId().getContent());
	}

	public GraphEdge getGraphEdgeWith(Integer fromId,Integer toId){
		String where=" where "+AbstractFactory.FROM_NODE_ID+"="+fromId
				+" and "+AbstractFactory.TO_NODE_ID+"="+toId;
		try{
			List<GraphEdge> tmp=this.getEdgesWith(where);
			if(tmp.isEmpty())
				return null;
			else
				return tmp.get(0);
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
	}

	public List<GraphEdge> getEdgesFrom(Integer nodeId) {
		String where = "where " + AbstractFactory.FROM_NODE_ID + "=" + nodeId;
		try {
			return this.getEdgesWith(where);
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}

	public List<GraphEdge> getEdgesTo(Integer nodeId) {
		String where = "where " + AbstractFactory.TO_NODE_ID + "=" + nodeId;
		try {
			return this.getEdgesWith(where);
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}

	private List<GraphEdge> getEdgesWith(String where) throws SQLException {
		List<GraphEdge> result = new ArrayList<GraphEdge>();
		for (Edge edge : this.getEdgeFactory().select(where))
			result.add(this.getGraphEdgeBy(edge.getId().getContent()));

		return result;
	}

	public Map<String, String> loadGraphEdgeAttributeMap(Long graphEdgeId) throws SQLException {
		Map<String, String> result = new HashMap<String, String>();
		List<EdgeAttributeValue> values = this.getEdgeAttributeValueFactory().select(
				"where " + AbstractFactory.EDGE_ID + "=" + graphEdgeId);
		for (EdgeAttributeValue value : values)
			result.put(this.getEdgeAttributeNameFactory().getBean(value.getEdgeAttributeNameId())
					.getEdgeAttributeName().toString(), value.getEdgeAttributeValue().toString());
		return result;
	}

	public GraphEdge getGraphEdgeBy(Long edgeId) {
		GraphEdge result = new GraphEdge();
		result.setEdgeId(edgeId);
		Edge edge;
		try {
			result.setAttributeMap(this.loadGraphEdgeAttributeMap(edgeId));
			edge = this.getEdgeFactory().getBean(new SqlInteger<Long>((short) 16, edgeId));
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
		result.setFromNodeId(edge.getFromNodeId().getContent());
		result.setToNodeId(edge.getToNodeId().getContent());

		return result;
	}

	public Boolean deleteGraphEdge(GraphEdge graphEdge) {
		Boolean result = true;
		if (null == graphEdge.getEdgeId() || null == graphEdge.getFromNodeId() || null == graphEdge.getToNodeId())
			return false;

		for (String key : graphEdge.getAttributeMap().keySet())
			if (result) {
				EdgeAttributeName edgeAttributeName = this.getEdgeAttributeNameFactory().getBy(key);
				Integer keyId = null == edgeAttributeName ? null : edgeAttributeName.getId().getContent();
				if (null != keyId)
					try {
						synchronized (this.getEdgeAttributeValueFactory()) {
							this.getEdgeAttributeValueFactory().delete(
									this.getEdgeAttributeValueFactory().getBy(graphEdge.getEdgeId(), keyId));
						}
					} catch (SQLException e) {
						result = false;
						e.printStackTrace();
					}
			}

		if (result) {
			Edge edge = new Edge(new SqlInteger<Integer>((short) 8, graphEdge.getFromNodeId()),
					new SqlInteger<Integer>((short) 8, graphEdge.getToNodeId()));
			edge.setId(new SqlInteger<Long>((short) 16, graphEdge.getEdgeId()));
			synchronized (this.getEdgeFactory()) {
				try {
					this.getEdgeFactory().delete(edge);
				} catch (SQLException e) {
					result = false;
					this.getEdgeFactory().rollback();
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	public Long saveGraphEdge(GraphEdge graphEdge) {
		Boolean result = true;
		result = graphEdge.getFromNodeId() != null && graphEdge.getToNodeId() != null;
		if (result) {
			graphEdge.setEdgeId(this.saveEdge(graphEdge));
			result = null != graphEdge.getEdgeId();
		} else
			return graphEdge.getEdgeId();

		if (result)
			synchronized (this.getEdgeAttributeValueFactory()) {
				try {
					Map<String, String> attrMap = this.loadGraphEdgeAttributeMap(graphEdge.getEdgeId());
					for (String key : graphEdge.getAttributeMap().keySet()) {
						Integer keyId = null;
						EdgeAttributeName attributeName = this.getEdgeAttributeNameFactory().getBy(key);
						keyId = (null == attributeName) ? null : attributeName.getId().getContent();
						if (null == keyId)
							keyId = this.getAttributeNameId(key);

						if (null != keyId)
							result = (null != this.saveAttributeValue(graphEdge, keyId, key));
						else
							return graphEdge.getEdgeId();

						attrMap.remove(key);
					}

					for (String key : attrMap.keySet())
						this.getEdgeAttributeValueFactory().delete(
								this.getEdgeAttributeValueFactory().getBy(graphEdge.getEdgeId(),
										this.getEdgeAttributeNameFactory().getBy(key).getId().getContent()));
				} catch (SQLException e) {
					e.printStackTrace();
					return null;
				}
			}
		return graphEdge.getEdgeId();
	}

	private Long saveAttributeValue(GraphEdge graphEdge, Integer keyId, String key) {
		synchronized (this.getEdgeAttributeValueFactory()) {
			try {
				if (null == this.getEdgeAttributeValueFactory().getBy(graphEdge.getEdgeId(), keyId))
					return this
							.getEdgeAttributeValueFactory()
							.insert(new EdgeAttributeValue(new SqlInteger<Integer>((short) 8, keyId),
									new SqlInteger<Long>((short) 16, graphEdge.getEdgeId()), new SqlChars<String>(
											(short) 255, graphEdge.getAttribute(key)))).getId().getContent();
				else {
					EdgeAttributeValue value = new EdgeAttributeValue(new SqlInteger<Integer>((short) 8, keyId),
							new SqlInteger<Long>((short) 16, graphEdge.getEdgeId()), new SqlChars<String>((short) 255,
									graphEdge.getAttribute(key)));
					value.setId(new SqlInteger<Long>((short) 18, this.getEdgeAttributeValueFactory()
							.getBy(graphEdge.getEdgeId(), keyId).getId().getContent()));
					this.getEdgeAttributeValueFactory().update(value);
					return value.getId().getContent();
				}
			} catch (SQLException e) {
				this.getEdgeAttributeValueFactory().rollback();
				e.printStackTrace();
				return null;
			}
		}
	}

	private Long saveEdge(GraphEdge graphEdge) {
		synchronized (this.getEdgeFactory()) {
			if (null == graphEdge.getEdgeId())
				try {
					return this
							.getEdgeFactory()
							.insert(new Edge(new SqlInteger<Integer>((short) 8, graphEdge.getFromNodeId()),
									new SqlInteger<Integer>((short) 8, graphEdge.getToNodeId()))).getId().getContent();
				} catch (SQLException e) {
					this.getEdgeFactory().rollback();
					e.printStackTrace();
					return null;
				}
			else
				return graphEdge.getEdgeId();
		}
	}

	public Integer addAttributeNameToGraph(String name) {
		return this.getAttributeNameId(name);
	}

	private Integer getAttributeNameId(String name) {
		synchronized (this.getEdgeAttributeNameFactory()) {
			try {
				return this.getEdgeAttributeNameFactory()
						.insert(new EdgeAttributeName(new SqlChars<String>((short) 255, name))).getId().getContent();
			} catch (SQLException e) {
				this.getEdgeAttributeNameFactory().rollback();
				e.printStackTrace();
				return null;
			}
		}
	}

	public EdgeFactory getEdgeFactory() {
		return edgeFactory;
	}

	public void setEdgeFactory(EdgeFactory edgeFactory) {
		this.edgeFactory = edgeFactory;
	}

	public EdgeAttributeNameFactory getEdgeAttributeNameFactory() {
		return edgeAttributeNameFactory;
	}

	public void setEdgeAttributeNameFactory(EdgeAttributeNameFactory edgeAttributeNameFactory) {
		this.edgeAttributeNameFactory = edgeAttributeNameFactory;
	}

	public EdgeAttributeValueFactory getEdgeAttributeValueFactory() {
		return edgeAttributeValueFactory;
	}

	public void setEdgeAttributeValueFactory(EdgeAttributeValueFactory edgeAttributeValueFactory) {
		this.edgeAttributeValueFactory = edgeAttributeValueFactory;
	}
}
